package openjdk.concurrent;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 字面意思回环栅栏，通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后，
 * CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier，当调用await()方法之后，线程就处于barrier了。
 * <p>
 * CyclicBarrier类位于java.util.concurrent包下，CyclicBarrier提供2个构造器：
 * public CyclicBarrier(int parties) { }
 * public CyclicBarrier(int parties, Runnable barrierAction) { }
 * <p>
 * 参数parties指让多少个线程或者任务等待至barrier状态；参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
 * <p>
 * <p>
 * CyclicBarrier中最重要的方法就是await方法，它有2个重载版本：
 * <p>
 * public int await() throws InterruptedException, BrokenBarrierException { };
 * public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
 * <p>
 * 第一个版本比较常用，用来挂起当前线程，直至所有线程都到达barrier状态再同时执行后续任务；
 * 第二个版本是让这些线程等待至一定的时间，如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
 * <p>
 * 在所有参与者都已经在此 barrier 上调用 await方法之前，将一直等待。如果当前线程不是将到达的最后一个线程，出于调度目的，将禁用它，
 * 且在发生以下情况之一前，该线程将一直处于休眠状态：
 * 最后一个线程到达；或者
 * 其他某个线程中断当前线程；或者
 * 其他某个线程中断另一个等待线程；或者
 * 其他某个线程在等待 barrier 时超时；或者
 * 其他某个线程在此 barrier 上调用 reset()。
 * 如果当前线程：
 * 在进入此方法时已经设置了该线程的中断状态；或者
 * 在等待时被中断
 * <p>
 * 则抛出 InterruptedException，并且清除当前线程的已中断状态。如果在线程处于等待状态时 barrier 被 reset()，
 * 或者在调用 await 时 barrier 被损坏，抑或任意一个线程正处于等待状态，则抛出 BrokenBarrierException 异常。
 * 如果任何线程在等待时被 中断，则其他所有等待线程都将抛出 BrokenBarrierException 异常，并将 barrier 置于损坏状态。
 * 如果当前线程是最后一个将要到达的线程，并且构造方法中提供了一个非空的屏障操作，则在允许其他线程继续运行之前，当前线程将运行该操作。
 * 如果在执行屏障操作过程中发生异常，则该异常将传播到当前线程中，并将 barrier 置于损坏状态。
 * <p>
 * <p>
 * 返回：
 * 到达的当前线程的索引，其中，索引 getParties() - 1 指示将到达的第一个线程，零指示最后一个到达的线程
 * 抛出：
 * InterruptedException - 如果当前线程在等待时被中断
 * BrokenBarrierException - 如果另一个 线程在当前线程等待时被中断或超时，或者重置了 barrier，或者在调用 await 时
 * barrier 被损坏，抑或由于异常而导致屏障操作（如果存在）失败。
 * <p>
 * <p>
 * <p>
 * <p>
 * <p>
 * 应用场景：
 * 一个大型的任务，常常需要分配好多子任务去执行，只有当所有子任务都执行完成时候，才能执行主任务，这时候可以选择CyclicBarrier
 * <p>
 * 示例：
 * CyclicBarrier 表示大家彼此等待，大家集合好后才开始出发，分散活动后又在i指定地点集合碰面，这就好比整个公司的人员利用周末时间集体郊游一样，
 * 先各自从家出发到公司集合后，再同时出发到公园游玩，在指定地点集合后再同时开始就餐……
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        test01();
    }


    private static void test01() {
        ExecutorService es = Executors.newCachedThreadPool();

        final CyclicBarrier cb = new CyclicBarrier(3);
        for (int i = 0; i < 3; i++) {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep((long) (Math.random() * 5000));
                        System.out.println("线程" + Thread.currentThread().getName() +
                                " 到达 山脚下，当前已有" + (cb.getNumberWaiting() + 1) + "个已到达" +
                                (cb.getNumberWaiting() == 2 ? ",都到齐了，开始AA" : "正在等候"));
                        try {
                            cb.await();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }

                        Thread.sleep((long) (Math.random() * 5000));
                        System.out.println("线程" + Thread.currentThread().getName() +
                                " 到达 山腰，当前已有" + (cb.getNumberWaiting() + 1) + "个已到达" +
                                (cb.getNumberWaiting() == 2 ? ",都到齐了，开始BB" : "正在等候"));
                        try {
                            cb.await();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                        Thread.sleep((long) (Math.random() * 5000));
                        System.out.println("线程" + Thread.currentThread().getName() +
                                " 到达 山顶，当前已有" + (cb.getNumberWaiting() + 1) + "个已到达" +
                                (cb.getNumberWaiting() == 2 ? ",都到齐了，开始CC" : "正在等候"));
                        try {
                            cb.await();
                        } catch (BrokenBarrierException e) {
                            e.printStackTrace();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            es.execute(r);
        }
        es.shutdown();
    }
}
